package me.ezjs.framework.core.model;

import me.ezjs.framework.util.JSONUtil;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 搜索的封装类
 *
 * @author zhujs
 */
public class DAOFilter implements Cloneable {

    public DAOFilter() {

    }


    public DAOFilter(boolean isOrSearch) {
        if (isOrSearch) {
            paramTree.useOrRelation4Root();
        }
    }

    public void closeFrontParam() {
        this.allowFrontParam = false;
    }

    ///////////////////////////////////////////////////////////////////////////
    // for select
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 自定义 select 语句, TODO:完成select语句的封装.
     * <p/>
     * 使用obj.*的原因是因为可以直接将结果转换为对象. 而使用obj.id, obj.name时只能返回为map,不是对象.
     */
    private List<String> selectList = null;

    public List<String> getSelect() {
        return selectList;
    }

    public void setSelect(List<String> selectList) {
        this.selectList = selectList;
    }

    public DAOFilter addSelect(String s) {
        if (this.selectList == null) {
            this.selectList = new ArrayList<String>();
        }
        this.selectList.add(s);
        return this;
    }

    ///////////////////////////////////////////////////////////////////////////
    // for where
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 单一查询.Map仅用于前台的封装
     * <p/>
     * LIKE.0
     * <p/>
     * <pre>
     * < input type="hidden" name="p['fieldName'].value" value="查询内容" />
     * 或者
     * < input type="hidden" name="p['fieldName_LK'].value" value="查询内容" />
     * </pre>
     * <p/>
     * EQUAL.1
     * <p/>
     * <pre>
     * < input type="hidden" name="p['fieldName_EQ'].value" value="查询内容" />
     * </pre>
     * <p/>
     */
    private Map<String, Parameter> s;

    private boolean allowFrontParam = true;

    public Map<String, Parameter> getS() {
        return s;
    }

    public void setS(Map<String, Parameter> s) {
        this.s = s;
    }

    public boolean isAllowFrontParam() {
        return allowFrontParam;
    }

    /**
     * 所有的对象进行过滤后存放在paramTree中
     */
    private ParamTree paramTree = new ParamTree();// 默认为and查询

    public ParamTree getParamTree() {
        return paramTree;
    }

    /**
     * 增加单一查询条件,支持大多数运算符.
     * <p/>
     *
     * @param value     字段值
     * @param op        查询运算
     * @param fieldName 字段名
     * @return {@code DAOFilter} DAOFilter
     */
    public DAOFilter addSearch(String fieldName, Operate op, Object value) {
        Parameter pm = new Parameter(fieldName, value, op);
        paramTree.addParameter(pm);
        return this;
    }

    public DAOFilter addSearch(Parameter pm) {
        paramTree.addParameter(pm);
        return this;
    }


    public DAOFilter addSearchSameKey(String fieldName, Operate op, Object... values) {
        for (Object val : values) {
            Parameter p = new Parameter(fieldName, val, op);
            paramTree.addParameter(p);
        }
        return this;
    }

    /**
     * 添加条件到根节点
     *
     * @param node
     * @return
     */
    public DAOFilter addSearch(ParamNode node) {
        paramTree.addNodes(node);
        return this;
    }

    /**
     * 区间查询.对between运算符的支持.
     * <p/>
     * 若begin为空,则自动转为小于end;若end为空则自动转为大于begin.
     * <p/>
     * 支持页面的封装.封装在名为"r"的map中.
     * <p/>
     * <pre>
     * < input type="hidden" name="r['fieldName'].begin" value="开始值" />
     * </pre>
     * <p/>
     * <pre>
     * < input type="hidden" name="r['fieldName'].end" value="结束值" />
     * </pre>
     */
    public DAOFilter addRegion(String fieldName, Object begin, Object end) {
        Parameter pm = new Parameter(fieldName, begin, end);
        paramTree.addParameter(pm);
        return this;
    }


///////////////////////////////////////////////////////////////////////////
// for sort
///////////////////////////////////////////////////////////////////////////

    /**
     * sortList,如果支持前端条件获取,则需要将s中的排序条件添加到sl的前面.
     */
    private List<Sort> sl = new LinkedList<Sort>();

    public List<Sort> getSl() {
        return sl;
    }

    public void setSl(List<Sort> sl) {
        this.sl = sl;
    }

    /**
     * 添加排序条件,支持多字段排序,排序优先级同添加的先后顺序.
     *
     * @param field 字段名
     * @param desc  是否降序
     * @return
     */
    public DAOFilter addSort(String field, boolean desc) {
        sl.add(new Sort(field, desc));
        return this;
    }

    ///////////////////////////////////////////////////////////////////////////
    // other
    ///////////////////////////////////////////////////////////////////////////


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        //select
        //where
        sb.append(paramTree.toString());
        //sort
        return sb.toString();
    }

    public DAOFilter deepClone() {

        try {
            // 将对象写到流里
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oo;
            oo = new ObjectOutputStream(bo);
            oo.writeObject(this);
            // 从流里读出来
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            return (DAOFilter) (oi.readObject());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    ///////////////////////////////////////////////////////////////////////////
    // test
    ///////////////////////////////////////////////////////////////////////////
    public static void main(String[] args) {
        Parameter p1 = new Parameter("sMonth", 3, Operate.LESSEQUAL);
        Parameter p2 = new Parameter("bMonth", 4, Operate.GREATEQUAL);

        Parameter p3 = new Parameter("sMonth", 5, Operate.LESSEQUAL);
        Parameter p4 = new Parameter("bMonth", 6, Operate.GREATEQUAL);
        Parameter p5 = new Parameter("bMonth", 7, Operate.GREATEQUAL);

        // 组成 (p1 & p2) or (p3 & p4)
        DAOFilter filter = new DAOFilter(true);
        filter.addSearch(new ParamNode(ParamNode.Relation.and, p1, p2));
        filter.addSearch(new ParamNode(ParamNode.Relation.and, p3, p4, p5));
        System.out.println(filter);

    }

}
